package com.example.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.exception.NoStockException;
import com.example.common.mq.StockDetailTo;
import com.example.common.mq.StockLockedTo;
import com.example.common.to.OrderTo;
import com.example.common.to.hasStockVo;
import com.example.common.utils.PageUtils;
import com.example.common.utils.Query;
import com.example.common.utils.R;
import com.example.gulimall.ware.dao.WareSkuDao;
import com.example.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.example.gulimall.ware.entity.WareOrderTaskEntity;
import com.example.gulimall.ware.entity.WareSkuEntity;
import com.example.gulimall.ware.feign.OrderFeignService;
import com.example.gulimall.ware.feign.ProductFeignService;
import com.example.gulimall.ware.service.WareOrderTaskDetailService;
import com.example.gulimall.ware.service.WareOrderTaskService;
import com.example.gulimall.ware.service.WareSkuService;
import com.example.gulimall.ware.vo.OrderEntityVo;
import com.example.gulimall.ware.vo.OrderItemVo;
import com.example.gulimall.ware.vo.SkuWareHasStockVo;
import com.example.gulimall.ware.vo.WareSkuLockVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;



@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WareOrderTaskService taskService;

    @Autowired
    WareOrderTaskDetailService detailService;

    @Autowired
    OrderFeignService orderFeignService;


    private void unLockStock(Long skuId, Long wareId, Integer num, Long taskDetailedId){
        wareSkuDao.unLockedStock(skuId,wareId,num);
        // 订单修改为已解锁
        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
        detailEntity.setId(taskDetailedId);
        detailEntity.setLockStatus(2);
        detailService.updateById(detailEntity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        String key = (String) params.get("key");
        if(!StringUtils.isEmpty(key)){
            wrapper.and((w)->{
               w.eq("purchase_id",key).or().eq("sku_id",key);
            });
        }


        String skuId = (String) params.get("skuId");
        if(!StringUtils.isEmpty(skuId)){
            wrapper.eq("sku_id",skuId);
        }

        String wareId = (String) params.get("wareId");
        if(!StringUtils.isEmpty(skuId)){
            wrapper.eq("ware_id",wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        List<WareSkuEntity> skuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if(skuEntities == null || skuEntities.size() == 0){
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setStockLocked(0);
            try {
                R info = productFeignService.info(skuId);
                Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                if(info.getCode() == 0)
                    wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));
            }catch (Exception e){

            }
            wareSkuDao.insert(wareSkuEntity);
        }else
        wareSkuDao.addstock(skuId,wareId,skuNum);
    }

    @Override
    public List<hasStockVo> gethasStock(List<Long> skuIds) {

        return skuIds.stream().map(sku -> {
            hasStockVo vo = new hasStockVo();
            vo.setSkuId(sku);
            Long count = baseMapper.getSkuStock(sku);
            System.out.println("此时的库存是"+count);
            vo.setHasStock(count != null && count > 0);
            return vo;
        }).collect(Collectors.toList());
    }

    // 需要所库存的两个场景
    // 1. 下订单成功 订单过期没有支付被取消 要解锁库存
    // 2. 下订单成功 库存锁定成功 接下来任务失败 导致业务调度失败 导致订单回滚
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {

        //保存库存工作单的详情 这样方便我们解锁
        WareOrderTaskEntity orderTaskEntity = new WareOrderTaskEntity();
        orderTaskEntity.setOrderSn(vo.getOrderSn());
        taskService.save(orderTaskEntity);

        // 查询哪些地方有库存
        List<OrderItemVo> locks = vo.getLocks();
        List<SkuWareHasStockVo> stockVos = locks.stream().map(item -> {
            SkuWareHasStockVo stockVo = new SkuWareHasStockVo();
            Long skuId = item.getSkuId();
            // 查询skuId和库存
            stockVo.setSkuId(skuId);
            stockVo.setNum(item.getCount());
            List<Long> wareIds = baseMapper.listWareIdHasSkuStock(skuId);
            stockVo.setWareId(wareIds);
            return stockVo;
        }).collect(Collectors.toList());

        // 锁库存
        Boolean allLock = true;
        for (SkuWareHasStockVo stockVo : stockVos) {
            Boolean skuStocked = false;
            List<Long> wareId = stockVo.getWareId();
            Long skuId = stockVo.getSkuId();
            if(wareId == null && wareId.size() == 0)
                throw new NoStockException(skuId);
            // 如果都锁定成功 那么将当前商品 锁定的工作单发送给mq
            // 如果锁定失败 前面保存的工作单信息就回滚了 发送出去的消息 即使要解锁记录 查不到id 就不用解锁
            for (Long ware : wareId) {
                // 更新语句成功返回1 否则0
                Long count = baseMapper.lockSkuStock(skuId,ware,stockVo.getNum());
                if(count > 0){
                    skuStocked = true;
                    WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity(null,skuId,"",stockVo.getNum(),orderTaskEntity.getId(),ware,1);
                    detailService.save(taskDetailEntity);
                    // 告诉MQ库存锁定成功
                    // 如果只发DetailId 消息不够 到时候回滚了 你数据都没了你啥都不知道 也没办法解锁
                    // 所以需要把实体++++
                    StockLockedTo lockedTo = new StockLockedTo();
                    StockDetailTo detail = new StockDetailTo();
                    BeanUtils.copyProperties(taskDetailEntity,detail);
                    lockedTo.setId(orderTaskEntity.getId());
                    lockedTo.setDetail(detail);
                    rabbitTemplate.convertAndSend("stock-event-exchange",
                            "stock.locked",lockedTo);
                    break;
                }
            }
            //没锁住
            if(!skuStocked)
                throw new NoStockException(skuId);
        }

        //

        return true;
    }

    @Override
    public void unLockStock(StockLockedTo to) throws Exception {
        //System.out.println("收到解锁请求");
        Long id = to.getId(); // 工作单Id
        StockDetailTo detail = to.getDetail();
        Long skuId = detail.getSkuId(); // SkuId
        Long detailId = detail.getId(); // 工作单详情Id
        // 1.解锁库存信息
        // 首先需要查询锁定库存信息
        // 没有：库存锁定失败 数据库回滚 不用解锁
        // 有：没发生回滚 要不要进行解锁要分两种情况
        // 没有这个订单 必须解锁
        // 有这个订单 要看状态 已取消就解锁 没取消不能解锁
        // 如果如果！！！！如果解锁失败了！！！不要删消息！！启动手动ACK机制
        WareOrderTaskDetailEntity byId = detailService.getById(detailId);
        if(byId == null){
            // 无需解锁
            // channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }else{
            // 需要解锁
            Long toId = to.getId();
            WareOrderTaskEntity taskEntity = taskService.getById(toId);
            String orderSn = taskEntity.getOrderSn();
            R r = orderFeignService.getOrderStatus(orderSn);
            if(r.getCode() == 0){
                OrderEntityVo entityVo = r.getData(new TypeReference<OrderEntityVo>() {});
                // 其他地方炸了
                if(entityVo == null || entityVo.getStatus() == 4){
                    //订单被取消了 才能解锁库存
                    if(byId.getLockStatus() == 1)
                    unLockStock(skuId,detail.getWareId(),detail.getSkuNum(),detailId);
                    // channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                }
            }else{
                throw new Exception("远程服务失败");
                //重新发会给队列
                //channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
            }
        }
    }

    // 防止订单服务卡顿 导致订单状态一直改不了 导致没办法解锁库存
    @Override
    @Transactional
    public void unLockStock(OrderTo to) {
        // 查库存工作单的情况 然后开始解锁
        String orderSn = to.getOrderSn();
        WareOrderTaskEntity entity = taskService.getOrderTaskByOrderSn(orderSn);
        Long id = entity.getId();
        List<WareOrderTaskDetailEntity> list = detailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", id).eq("lock_status", 1));
        for (WareOrderTaskDetailEntity wareEntity : list) {
            unLockStock(wareEntity.getSkuId(),wareEntity.getWareId(),wareEntity.getSkuNum(),wareEntity.getId());
        }

    }

}